﻿/**************************************************************
 *
 * 唯一标识：12a1825e-9b5c-464f-a418-8ac46f63ff1f
 * 命名空间：MediatR
 * 创建时间：2023/7/28 16:05:13
 * 机器名称：DESKTOP-S0D075D
 * 创建者：antho
 * 电子邮箱：fengqinhua2016@163.com
 * 描述：
 *
 **************************************************************/

using Microsoft.EntityFrameworkCore;
using Sgr.Domain.Entities;
using Sgr.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MediatR
{
    /// <summary>
    /// IMediator 扩展函数
    /// </summary>
    public static class MediatorExtensions
    {
        // 默认最大循环次数，可以根据实际需求调整
        private const int MaxDispatchAttempts = 3;

        /// <summary>
        /// 发布领域事件
        /// </summary>
        /// <param name="mediator">中介者实例</param>
        /// <param name="ctx">数据库上下文</param>
        /// <param name="maxAttempts">最大尝试次数，默认为3次</param>
        /// <returns>异步任务</returns>
        /// <exception cref="InvalidOperationException">当超过最大尝试次数时抛出</exception>
        public static async Task DispatchDomainEventsAsync(
            this IMediator mediator,
            DbContext ctx,
            int maxAttempts = MaxDispatchAttempts)
        {
            var attemptCount = 0;
            // 持续处理直到没有新的领域事件产生
            while (true)
            {
                attemptCount++;
                if (attemptCount > maxAttempts)
                {
                    throw new BusinessException(
                        $"领域事件循环处理超过最大尝试次数({maxAttempts})，可能存在循环依赖或异常导致的无限事件生成。");
                }

                // 获取所有包含领域事件的实体
                var domainEntities = ctx.ChangeTracker
                    .Entries<IDomainEventManage>()
                    .Where(x => x.Entity.DomainEvents != null && x.Entity.DomainEvents.Any());

                // 如果没有更多的领域事件，退出循环
                if (!domainEntities.Any())
                    break;

                // 收集所有领域事件
                var domainEvents = domainEntities
                    .SelectMany(x => x.Entity.DomainEvents)
                    .ToList();

                // 清除已收集的领域事件
                domainEntities.ToList()
                    .ForEach(entity => entity.Entity.ClearDomainEvents());

                // 发布事件
                foreach (var domainEvent in domainEvents)
                {
                    await mediator.Publish(domainEvent);
                }
            }
        }
    }
}